home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Communications / PPPMonitor1.16 / Source / ExecMonitor.m < prev    next >
Text File  |  1996-03-07  |  30KB  |  1,042 lines

  1. // -------------------------------------------------------------------------------------
  2. // ExecMonitor.m
  3. // (Indent:4, Tabs:4)
  4. // -------------------------------------------------------------------------------------
  5. // Copyright 1996 Persistent Technologies, Inc. - all rights reserved
  6. // -------------------------------------------------------------------------------------
  7. // This source code comes with no warranty of any kind, and the user assumes all 
  8. // responsibility for its use.
  9. // -------------------------------------------------------------------------------------
  10. #import <libc.h>
  11. #import <stdlib.h>
  12. #import <c.h>
  13. #import <errno.h>
  14. #import <ctype.h>
  15. #import <math.h>
  16. #import <sys/param.h>
  17. #import <sys/types.h>
  18. #import <sys/time.h>
  19. #import <sys/wait.h>
  20. #import <sys/resource.h>
  21. #import <dpsclient/dpsNeXT.h>
  22. #import <appkit/Listener.h>
  23. #import <appkit/appkit.h>
  24. #import "PPPMonitor.h"
  25. #import "ExecScrollText.h"
  26. #import "ExecMonitor.h"
  27.  
  28. #define CHKPPPSOCK
  29. #ifdef CHKPPPSOCK
  30. #import <sys/socket.h>
  31. #import <sys/ioctl.h>
  32. #import <netdb.h>
  33. #import <net/route.h>
  34. #import <net/if.h>
  35. #endif
  36.  
  37. // -------------------------------------------------------------------------------------
  38. // local vars
  39. static id globalMonitor = (id)nil;    // ExecMonitor instance
  40. static int _testMode_ = 0;            // test mode flag
  41.  
  42. // -------------------------------------------------------------------------------------
  43. // ppp 'system' commands
  44. #define SYS_PPP_ISLOADED "/usr/etc/kl_util -s | /bin/grep ' ppp Loaded' "
  45. #define SYS_PPP_ISUP     "/usr/etc/ifconfig ppp0 | /bin/grep RUNNING "
  46. #define SYS_PPPD_RUNNING "ps -ax | egrep '(/pppd )' | egrep -v 'egrep' "
  47.  
  48. // -------------------------------------------------------------------------------------
  49. // play sounds
  50. #define PLAY(S)                { if (S) [[Sound findSoundFor:(S)] play]; }
  51.  
  52. // -------------------------------------------------------------------------------------
  53. // network applications (default when "MiniShelf" default-db is not available)
  54. #define MAX_SHELFSIZE        10
  55. static const char *netAppList = (char*)0;
  56. static const char *netApps[MAX_SHELFSIZE + 1] = { (char*)0 };
  57. static int netAppCount = 0;
  58.  
  59. // -------------------------------------------------------------------------------------
  60. // misc defines
  61. #define LCURL                '{'
  62. #define RCURL                '}'
  63. #define LPREN                '('
  64. #define RPREN                ')'
  65. #define SYSTEM(C)            [ExecRunCommand system:(C)]
  66.  
  67. // -------------------------------------------------------------------------------------
  68. // Mini-Shelf icon cell
  69.  
  70. @interface TileButtonCell : ButtonCell 
  71. { BOOL _noDots; }
  72. - (void)setDrawDots:(BOOL)flag;
  73. @end
  74.  
  75. /* highlight rectangle */
  76. static void _hiliteRect(const NXRect *r)
  77. {
  78.     PSgsave();
  79.     PSsetalpha(0.5);
  80.     PSsetgray(NX_WHITE);
  81.     PScompositerect(r->origin.x,r->origin.y,r->size.width,r->size.height,NX_SOVER);
  82.     PSgrestore();
  83. }
  84.  
  85. /* load special images used for minishelf */
  86. static NXImage *miniShelfTile = nil, *shelfTile = nil;
  87. static NXImage *miniShelfDots = nil, *shelfDots = nil;
  88. static void _loadMiniShelfIcons(void)
  89. {
  90.     {
  91.     const char *tile = ICO_SHELFTILE;
  92.     BOOL _tile = (tile && !strcasecmp(tile,"yes"))? YES : NO;
  93.     if (!miniShelfTile && _tile) miniShelfTile = [NXImage findImageNamed:"tile"]; 
  94.     shelfTile = _tile? miniShelfTile : nil;
  95.     }
  96.     {
  97.     const char *dots = ICO_SHELFDOTS;
  98.     BOOL _dots = (dots && !strcasecmp(dots,"yes"))? YES : NO;
  99.     if (!miniShelfDots && _dots) miniShelfDots = [NXImage findImageNamed:"dots"]; 
  100.     shelfDots = _dots? miniShelfDots : nil;
  101.     }
  102. }
  103.  
  104. // -------------------------------------------------------------------------------------
  105. // private functions
  106.  
  107. /* return interval timer value */
  108. long _intervalTime(void)
  109. {
  110.     struct timeval tp;
  111.     gettimeofday(&tp, NULL);
  112.     return tp.tv_sec;
  113. }
  114.  
  115. /* return start of command line */
  116. const char *_cmdLine(const char *cmd)
  117. {
  118.     const char *n = cmd;
  119.     while (*n && isspace(*n)) n++;
  120.     if ((*n == LPREN) || (*n == LCURL)) {
  121.         for (n++; *n && ((*n != RPREN) && (*n != RCURL)); n++);
  122.         if (*n) n++;
  123.     }
  124.     while (*n && isspace(*n)) n++;
  125.     return n;
  126. }
  127.  
  128. /* return command name */
  129. const char *_getCmdFile(char *buff, const char *cmd)
  130. {
  131.     const char *n2, *n1 = _cmdLine(cmd);
  132.     for (n2 = n1; *n2 && !isspace(*n2); n2++); // scan for space
  133.     strncpy(buff, n1, n2 - n1);
  134.     buff[n2 - n1] = 0;
  135.     return buff;
  136. }
  137.  
  138. /* return command name */
  139. const char *_getCmdName(char *buff, const char *cmd)
  140. {
  141.     const char *n = cmd;
  142.     while (*n && isspace(*n)) n++;
  143.     if ((*n == LPREN) || (*n == LCURL)) {
  144.         char *b = buff;
  145.         for (n++ ; *n && ((*n != RPREN) && (*n != RCURL)); n++) *b++ = *n;
  146.         *b = 0;
  147.     } else {
  148.         char tmp[1024], *name = strrchr((char*)_getCmdFile(tmp,n),'/');
  149.         strcpy(buff, (name?name+1:tmp));
  150.     }
  151.     return buff;
  152. }
  153.  
  154. /* return true if specified file ends with ".app" */
  155. BOOL _cmdIsApp(const char *cmd)
  156. {
  157.     char tmp[1024], *n = (char*)_getCmdFile(tmp,cmd);
  158.     const char *dot = strrchr(n, '.');
  159.     return (!dot || strcmp(dot,".app"))? NO : YES;
  160. }
  161.  
  162. // -------------------------------------------------------------------------------------
  163. @implementation ExecMonitor
  164.  
  165. // -------------------------------------------------------------------------------------
  166.  
  167. /* show message */
  168. - (void)message:(BOOL)isError:(const char*)fmt, ...
  169. {
  170.     char msg[1024], *m = msg;
  171.     va_list args;
  172.     if (isError) { m += strlen(strcpy(m,"ERROR: ")); }
  173.     va_start(args, fmt);
  174.     vsprintf(m, fmt, args);
  175.     va_end(args);
  176.     if (isError) {
  177.         if ([cmdMessage shouldDrawColor]) [cmdMessage setTextColor:NX_COLORRED];
  178.         else [cmdMessage setTextGray:NX_WHITE];
  179.     } else {
  180.         if ([cmdMessage shouldDrawColor]) [cmdMessage setTextColor:NX_COLORDKGRAY];
  181.         else [cmdMessage setTextGray:NX_DKGRAY];
  182.     }
  183.     [cmdMessage setStringValue:msg];
  184. }
  185.  
  186. // -------------------------------------------------------------------------------------
  187. // polling/timer support
  188.  
  189. /* target/action structure */
  190. typedef struct {
  191.     BOOL            abort;
  192.     id                self;
  193.     SEL                meth;
  194.     void            *arg;
  195.     DPSTimedEntry    timerTag;
  196. } _timerHandlerData_t;
  197.  
  198. /* periodic timer handler */
  199. static void _timerHandler(DPSTimedEntry tag, double now, void *userData)
  200. {
  201.     _timerHandlerData_t *t = (_timerHandlerData_t*)userData;
  202.     if (t->abort || ![t->self perform:t->meth with:(id)(t->arg)]) {
  203.         [t->self perform:@selector(_stopTimer:) with:(id)t];
  204.     }
  205. }
  206.  
  207. /* start periodic timer */
  208. - (_timerHandlerData_t*)_startTimer:(SEL)theSelector arg:(void*)arg freq:(float)freq
  209. {
  210.     _timerHandlerData_t *t = (_timerHandlerData_t*)malloc(sizeof(_timerHandlerData_t));
  211.     t->abort    = NO;
  212.     t->self     = self;
  213.     t->meth     = theSelector;
  214.     t->arg      = arg;
  215.     t->timerTag = DPSAddTimedEntry((double)freq,_timerHandler,(void*)t,30);
  216.     return t;
  217. }
  218.  
  219. /* stop timer */
  220. - (void)_stopTimer:(_timerHandlerData_t*)t
  221. {
  222.     if (t) {
  223.         DPSRemoveTimedEntry(t->timerTag);
  224.         free((void*)t);
  225.     }
  226. }
  227.  
  228. /* abbreviated delayed perform */
  229. - (void)_perform:(SEL)sel:(id)objId delay:(int)delay
  230. {
  231.     [self perform:sel with:objId afterDelay:delay cancelPrevious:YES];
  232. }
  233.  
  234. // -------------------------------------------------------------------------------------
  235. // fill mini-shelf
  236.  
  237. /* fill mini-shelf */
  238. - (void)_fillMiniShelf
  239. {
  240.     int i;
  241.     static ButtonCell *tileProto = nil;
  242.  
  243.     /* reset/load shelf tile icon */
  244.     _loadMiniShelfIcons();
  245.     
  246.     /* clear matrix */
  247.     [appMatrix removeRowAt:0 andFree:YES];
  248.     
  249.     /* parse/count shelf apps */
  250.     if (CMD_MINISHELF) {
  251.         char *n = (char*)(netAppList=STRDUP(CMD_MINISHELF));
  252.         netAppCount = 0;
  253.         while (*n) {
  254.             char *b1, *b2;
  255.             while ((*n == ';') || isspace(*n)) *(n++) = 0;
  256.             if (!*n) break;
  257.             netApps[netAppCount++] = b1 = n;
  258.             while (*n && (*n != ';')) n++;
  259.             b2 = n - 1;
  260.             if (*n) *n++ = 0;
  261.             while ((b2 > b1) && isspace(*b2)) *b2-- = 0;
  262.             if (netAppCount > MAX_SHELFSIZE) break;
  263.         }
  264.         netApps[netAppCount] = (char*)nil;
  265.     }
  266.  
  267.     /* set prototype */
  268.     if (!tileProto) {
  269. //        ButtonCell *proto = [appMatrix prototype];
  270.         tileProto = [[TileButtonCell alloc] initIconCell:"NXAppTile"];
  271.         [tileProto setHighlightsBy:NX_NONE];
  272.         [appMatrix setPrototype:tileProto];
  273.     }
  274.     
  275.     /* create app buttons */
  276.     [appMatrix renewRows:1 cols:netAppCount];
  277.     for (i = 0; i < netAppCount; i++) {
  278.         id btnCell = [appMatrix cellAt:0:i];
  279.         if (!netApps[i]) { netAppCount = i; break; }
  280.         if (_cmdIsApp(netApps[i])) {
  281.             char buff[1024], *cmdFile = (char*)_getCmdFile(buff,netApps[i]);
  282.             id appIcon = [[Application workspace] getIconForFile:cmdFile];
  283.             [btnCell setIconPosition:NX_ICONONLY];
  284.             [btnCell setImage:appIcon];
  285.         } else {
  286.             char cmdName[128];
  287.             _getCmdName(cmdName,netApps[i]);
  288.             [btnCell setIconPosition:NX_ICONABOVE];
  289.             [btnCell setIcon:"cmdButton"];
  290.             [btnCell setTitle:cmdName];
  291.         }
  292.         [btnCell setTag:i];
  293.     }
  294.     [appMatrix display];
  295.     
  296. }
  297.  
  298. /* called when new user defaults have been loaded */
  299. + (void)updateFromDefaults
  300. {
  301.     if (globalMonitor) [globalMonitor updateFromDefaults];
  302. }
  303.  
  304. /* called when new user defaults have been loaded */
  305. - (void)updateFromDefaults
  306. {
  307.     const char *showSec = FLG_SHOWSECONDS;
  308.     if (showSec) {
  309.         int val = atoi(showSec);
  310.         _showTileTime = ((val >= 0) && (val <= 2))? val : 0;
  311.     }
  312.     [self _fillMiniShelf];
  313.     [self enableConnectButton:nil];
  314.     [self tailLogFile:self];
  315. }
  316.  
  317. // -------------------------------------------------------------------------------------
  318. // object initialization
  319.  
  320. /* crete single shared instance */
  321. + (id)sharedPPPMonitor
  322. {
  323.     if (!globalMonitor) globalMonitor = [[self alloc] init];
  324.     return globalMonitor;
  325. }
  326.  
  327. /* init */
  328. - init
  329. {
  330.  
  331.     /* init super */
  332.     [super init];
  333.     exeWindow = (id)nil;
  334.     pppLogExeId = nil;
  335.     connectExeId = nil;
  336.     pingExeId = nil;
  337.     _alreadyConnected = NO;
  338.     _showTileTime = 0;
  339.     _isConnected = NO;
  340.     _isDisconnecting = NO;
  341.     _shutDown = NO;
  342.     _connectTime = 0L;
  343.     
  344.     /* clear timers */
  345.     pppdTimer  = (void*)nil;
  346.     clockTimer = (void*)nil;
  347.     ppingTimer = (void*)nil;
  348.     checkTimer = (void*)nil;
  349.  
  350.     /* test mode */
  351.     {
  352.     const char *val = [NSApp readDefault:"_TestMode_"];
  353.     _testMode_ = (val && (*val == '1'))? 1 : 0;
  354.     if (_testMode_) fprintf(stderr,"WARNING: PPPMonitor running in TEST mode.\n");
  355.     }
  356.     
  357.     /* load nib */
  358.     if (![NSApp loadNibSection:"ExecMonitor.nib" owner:self]) {
  359.         [NSApp errorPanel:"Could not load nib file 'ExecMonitor.nib'\n"
  360.             "(Check that the nib file exists and try again)"];
  361.         [NSApp delayedFree:self];
  362.         return nil;
  363.     }
  364.     
  365.     /* spot-check nib loading */
  366.     if (!exeWindow || !appMatrix) {
  367.         [NSApp errorPanel:"'ExecMonitor.nib' did not load properly\n"
  368.             "(The nib file has apparently been corrupted)"];
  369.         [NSApp delayedFree:self];
  370.         return nil;
  371.     }
  372.     
  373.     /* initially clear messages */
  374.     [pingMessage setStringValue:""];
  375.     [self message:0:""];
  376.     
  377.     /* set connection state */
  378.     if ([self isReallyConnected] || [self isPPPDRunning]) _alreadyConnected = YES;
  379.  
  380.     /* set mini-shelf target */
  381.     [appMatrix setTarget:self];
  382.     [appMatrix setDoubleAction:@selector(runApplication:)];
  383.     [appMatrix setAction:(SEL)0];
  384.  
  385.     /* update default settings */
  386.     [self updateFromDefaults];
  387.     
  388.     /* init panel */
  389.     [exeWindow setFrameAutosaveName:[NSApp appName]];
  390.     [exeWindow setDelegate:self];
  391.     [exeWindow makeKeyAndOrderFront:(id)nil];
  392.  
  393.     /* already connected */
  394.     if (_alreadyConnected) {
  395.         [self message:1:"Already connected (connection not made by this PPPMonitor)"];
  396.         [NSApp errorPanel:"Already connected:\n"
  397.                         "PPPMonitor does not currently support\n"
  398.                         "previously established connections."];
  399.     }
  400.     
  401.     return self;
  402. }
  403.  
  404. /* free */
  405. - _free:(id)sender { return [self free]; }
  406. - free
  407. {
  408.     _shutDown = YES;
  409.     if (pppLogExeId) [pppLogExeId killCommand];
  410.     if (connectExeId || pingExeId) {
  411.         if (connectExeId && (connectExeId != self)) {
  412.             // terminate ppp connections (actually, should already be terminated);
  413.         }
  414.         if (pingExeId) [pingExeId killCommand];
  415.         [self perform:@selector(_free:) with:self afterDelay:500 cancelPrevious:YES];
  416.         return (id)nil;
  417.     }
  418.     [exeWindow free];
  419.     globalMonitor = (id)nil;
  420.     return [super free];
  421. }
  422.  
  423. // -------------------------------------------------------------------------------------
  424. // shutting down
  425.  
  426. /* can shut down */
  427. - (BOOL)canShutDown
  428. {
  429.     return (connectExeId || pingExeId)? NO : YES;
  430. }
  431.  
  432. /* can shut down */
  433. + (BOOL)canShutDown
  434. {
  435.     return (!globalMonitor || [globalMonitor canShutDown])? YES : NO;
  436. }
  437.  
  438. /* return shut down state */
  439. - (BOOL)isShuttingDown
  440. {
  441.     return _shutDown;
  442. }
  443.  
  444. /* return connection flag state */
  445. + (BOOL)shutDown
  446. {
  447.     if (globalMonitor && ![globalMonitor isShuttingDown]) [globalMonitor free];
  448.     return globalMonitor? NO : YES;
  449. }
  450.  
  451. // -------------------------------------------------------------------------------------
  452. // connection state checks
  453.  
  454. /* check for true connection */
  455. - (BOOL)isReallyConnected
  456. {
  457. #ifdef CHKPPPSOCK
  458.     /* open socket to check ppp0 */
  459.     // (Thanks to Erik Doernenburg for his contributions to this section)
  460.     static int sock = -1; // maintain socket connection
  461.     if (sock == -1) sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
  462.     if (sock != -1) {
  463.         struct ifreq ifr;
  464.         strcpy(ifr.ifr_name, "ppp0");
  465.         if (ioctl(sock,SIOCGIFFLAGS,&ifr) != -1) {
  466.             return (ifr.ifr_flags & IFF_UP)? YES : NO;
  467.         }
  468.     }
  469.     // fprintf(stdout,"DEBUG - PPPMonitor: ppp0 socket check failed ...\n");
  470. #endif
  471.     return SYSTEM(SYS_PPP_ISUP)? NO : YES; // fallback if all else fails
  472. }
  473.  
  474. /* check for true connection */
  475. - (BOOL)isPPPDRunning
  476. {
  477.     return SYSTEM(SYS_PPPD_RUNNING)? NO : YES;
  478. }
  479.  
  480. /* check for ppp loaded */
  481. - (BOOL)isPPPDriverLoaded
  482. {
  483.     return SYSTEM(SYS_PPP_ISLOADED)? NO : YES;
  484. }
  485.  
  486. /* enable/disable button options */
  487. - (void)enableConnectButton:(id)sender
  488. {
  489.     if (sender) { // delay
  490.         [btnConnect setEnabled:NO];
  491.         [self _perform:@selector(enableConnectButton:):nil delay:1000];
  492.         return;
  493.     } else
  494.     if (!_alreadyConnected) { // set 'Connect' button enabled state
  495.         if (!connectExeId && !_isConnected) {
  496.             [btnConnect setTitle:"Connect"];
  497.         } else
  498.         if ( connectExeId && !_isConnected) {
  499.             [btnConnect setTitle:"(Stop)"];
  500.         } else {
  501.             [btnConnect setTitle:"Disconnect"];
  502.         }
  503.         [btnConnect setEnabled:YES];
  504.     } else {
  505.         [btnConnect setEnabled:NO];
  506.     }
  507. }
  508.  
  509. // -------------------------------------------------------------------------------------
  510. // connect time keeper
  511.  
  512. /* create connect time image */
  513. - (void)setConnectTimeIconImage:(int)hh :(int)mm :(int)ss
  514. {
  515.     if (_isConnected) {
  516.         static NXImage *appImage = (id)nil;
  517.         static NXImage *conImage = (id)nil;
  518.         if (!appImage) appImage = [[NXImage alloc] initFromSection:"appConnect"];
  519.         if (!conImage) conImage = [NXImage findImageNamed:"appConnect"];
  520.         if ([appImage lockFocus]) {
  521.             char tm[16];
  522.             float w, h;
  523.             NXPoint orgPt = { 0.0, 0.0 }, txtPt = { 0.0, 6.0 };
  524.             [conImage composite:NX_SOVER toPoint:&orgPt];
  525.             if ((_showTileTime == 1) || (ss < 0)) sprintf(tm,"%d:%02d", hh, mm);
  526.             else sprintf(tm,"%d:%02d:%02d", hh, mm, ss);
  527.             PSsetgray(NX_BLACK); 
  528.             PSselectfont("Ohlfs",9.0);
  529.             PSstringwidth(tm,&w,&h);
  530.             txtPt.x = ((48.0 - w) / 2.0) + 1.0;
  531.             PSmoveto(txtPt.x,txtPt.y); 
  532.             PSshow(tm);
  533.             [appImage unlockFocus];
  534.             [NSApp setApplicationIconImage:appImage];
  535.         }
  536.     } else {
  537.         [NSApp setApplicationIconImage:[NXImage findImageNamed:"appIcon"]];
  538.     }
  539. }
  540.  
  541. /* TIMER: update connection timer */
  542. - (id)_updClockTime:(id)sender /* 'nil' sender resets timer */
  543. {
  544.     if (clockTimer && _isConnected) {
  545.         long interval = _intervalTime();
  546.         int hh = 0, mm = 0, ss = 0, tt;
  547.         static int lastMM = -1, lastMode = -1;
  548.         if (!sender || !_connectTime) { _connectTime = interval; }
  549.         tt = interval - _connectTime;
  550.         hh = tt / 3600, mm = (tt % 3600) / 60, ss = tt % 60;
  551.         [self message:0:"Connected  (%2d:%02d:%02d)", hh, mm, ss];
  552.         if (_showTileTime == 2) {
  553.             [self setConnectTimeIconImage:hh:mm:ss];
  554.         } else
  555.         if ((_showTileTime == 1) && ((lastMM != mm) || (lastMode != 1))) {
  556.             [self setConnectTimeIconImage:hh:mm:-1];
  557.             lastMM = mm;
  558.         } else
  559.         if ((_showTileTime == 0) && (lastMode != 0)) {
  560.             [NSApp setApplicationIconImage:[NXImage findImageNamed:"appIcon"]];
  561.         }
  562.         if (lastMode != _showTileTime) lastMode = _showTileTime;
  563.     }
  564.     return clockTimer? self : nil;
  565. }
  566.  
  567. /* start/stop timer */
  568. - (void)_enableConnectClock:(BOOL)flag
  569. {
  570.     if (clockTimer) {
  571.         [self _stopTimer:(_timerHandlerData_t*)clockTimer];
  572.         clockTimer = (void*)nil;
  573.         [NSApp setApplicationIconImage:[NXImage findImageNamed:"appIcon"]];
  574.     }
  575.     if (flag) {
  576.         _timerHandlerData_t *timer;
  577.         float freq = 0.99; // just shy of a second
  578.         [self _updClockTime:nil]; // reset timer
  579.         timer = [self _startTimer:@selector(_updClockTime:) arg:self freq:freq];
  580.         clockTimer = (void*)timer;
  581.     }
  582. }
  583.  
  584. // -------------------------------------------------------------------------------------
  585. // periodic command execution
  586.  
  587. /* TIMER: periodic ping */
  588. - (id)_periodicPing:(id)sender
  589. {
  590.     if (!ppingTimer || !_isConnected || !CMD_PING || _testMode_) {
  591.         ppingTimer = (void*)nil;
  592.     } else
  593.     if (!pingExeId) {
  594.         [pingMessage setStringValue:"Ping"];
  595.         pingExeId = [ExecRunCommand runCommand:CMD_PING output:self];
  596.         if (!pingExeId) [pingMessage setStringValue:"****"];
  597.     }
  598.     return ppingTimer? self : nil;
  599. }
  600.  
  601. /* start/stop ping */
  602. - (void)_enablePeriodicPing:(BOOL)flag
  603. {
  604.     if (ppingTimer) { // cancel if active
  605.         [self _stopTimer:(_timerHandlerData_t*)ppingTimer];
  606.         ppingTimer = (void*)nil;
  607.         if (pingExeId) [pingExeId killCommand];
  608.     }
  609.     if (flag) {
  610.         _timerHandlerData_t *timer;
  611.         int sec = CMD_PINGINTERVAL? atoi(CMD_PINGINTERVAL) : 3 * 60;
  612.         if (sec < 60) sec = 60;
  613.         timer = [self _startTimer:@selector(_periodicPing:) arg:self freq:(float)sec];
  614.         ppingTimer = timer;
  615.     }
  616. }
  617.  
  618. // -------------------------------------------------------------------------------------
  619. // connection monitor
  620.  
  621. /* TIMER: connection established */
  622. - (id)_checkConnect:(id)sender
  623. {
  624.     if (!connectExeId || _isConnected) { // if no connection in progress
  625.         checkTimer = (void*)nil;
  626.     } else
  627.     if (_testMode_ || [self isReallyConnected]) { // if connected
  628.         PLAY(SND_CONNECT);
  629.         _isConnected = YES;
  630.         [self enableConnectButton:self]; // delayed
  631.         [self _enablePeriodicPing:YES];
  632.         [self _enableConnectClock:YES];
  633.         checkTimer = (void*)nil;
  634.     }
  635.     return checkTimer? self : nil;
  636. }
  637.  
  638. /* start connection check */
  639. - (void)_enableCheckConnect:(BOOL)flag
  640. {
  641.     if (flag) {
  642.         _timerHandlerData_t *timer;
  643.         timer = [self _startTimer:@selector(_checkConnect:) arg:self freq:5.0];
  644.         checkTimer = timer;
  645.     } else
  646.     if (checkTimer) {
  647.         [self _stopTimer:(_timerHandlerData_t*)checkTimer];
  648.         checkTimer = (void*)nil;
  649.     }
  650. }
  651.  
  652. // -------------------------------------------------------------------------------------
  653. // pppd monitor
  654.  
  655. /* monitor '/pppd' command */
  656. - (id)_monitorPPPD:(id)sender
  657. {
  658.     if (![self isPPPDRunning]) {
  659.         pppdTimer = (void*)nil;
  660.         [self commandDidComplete:connectExeId withError:0];
  661.     }
  662.     return pppdTimer? self : nil;
  663. }
  664.  
  665. /* start monitor */
  666. - (void)_enablePPPDMonitor:(BOOL)flag
  667. {
  668.     if (pppdTimer) {
  669.         [self _stopTimer:(_timerHandlerData_t*)pppdTimer];
  670.         pppdTimer = (void*)nil;
  671.     }
  672.     if (flag && (connectExeId == self)) {
  673.         _timerHandlerData_t *timer;
  674.         float freq = 15.0;
  675.         timer = [self _startTimer:@selector(_monitorPPPD:) arg:self freq:freq];
  676.         pppdTimer = (void*)timer;
  677.     }
  678. }
  679.  
  680. // -------------------------------------------------------------------------------------
  681. // PPP log file monitor
  682.  
  683. /* print message to ppplog scroll text */
  684. - (void)logMessage:(BOOL)isError:(const char*)fmt, ...
  685. {
  686.     va_list args;
  687.     
  688.     /* text color (on a white background) */
  689.     if ([[pppLogScroll docView] shouldDrawColor]) {
  690.         if (isError) { [pppLogScroll setTextAttributeColor:NX_COLORRED]; }
  691.         else         { [pppLogScroll setTextAttributeGray:NX_DKGRAY]; }
  692.     } else {
  693.         if (isError) { [pppLogScroll setTextAttributeGray:NX_DKGRAY]; }
  694.         else         { [pppLogScroll setTextAttributeGray:NX_DKGRAY]; } // same gray
  695.     }
  696.     
  697.     /* print message */
  698.     va_start(args, fmt);
  699.     [pppLogScroll textPrintf:fmt args:args];
  700.     va_end(args);
  701.     
  702.     /* reset color to BLACK */
  703.     [pppLogScroll setTextAttributeGray:NX_BLACK];
  704. }
  705.  
  706. /* tail log file */
  707. - (void)tailLogFile:(id)sender
  708. {
  709.  
  710.     /* initialize pppLogScroll */
  711.     if (![pppLogScroll isMemberOf:[ExecScrollText class]]) {
  712.         pppLogScroll = [ExecScrollText newExecScrollText:pppLogScroll];
  713.         [pppLogScroll setDelegate:self];
  714.         [pppLogScroll setTab:[textFont getWidthOf:"        "] count:10];
  715.         [pppLogScroll textPrintf:"\n"];
  716.         [pppLogScroll clearScrollText];
  717.         [pppLogScroll setTextAttributeGray:NX_BLACK];
  718.     }
  719.     
  720.     /* start tail command */
  721.     if (!pppLogExeId) {
  722.         [pppLogScroll clearScrollText];
  723.         [pppLogScroll setTextAttributeGray:NX_BLACK];
  724.         if (CMD_TAILLOG) {
  725.             char *ld1 = "/usr/adm/";
  726.             char *ld2 = "/private/adm/";
  727.             char tailBuff[64], *tail = (char*)CMD_TAILLOG;
  728.             if ((strlen(tail) < 32) && !strchr(tail,' ') &&
  729.                 (!strncmp(tail,ld1,strlen(ld1)) || !strncmp(tail,ld2,strlen(ld2)))) {
  730.                 sprintf(tailBuff,"/usr/ucb/tail -1f %s;", tail);
  731.                 tail = tailBuff;
  732.             }
  733.             [self logMessage:0:"(%s)\n",tail];
  734.             pppLogExeId = [pppLogScroll runCommand:tail user:0];
  735.             if (!pppLogExeId) [self logMessage:1:"'tail' command failed to start.\n"];
  736.         } else {
  737.             [self logMessage:1:"'tail' command disabled (not specified).\n"];
  738.         }
  739.     }
  740.  
  741. }
  742.  
  743. /* clear text */
  744. - (void)clearLog:(id)sender
  745. {
  746.     [pppLogScroll clearScrollText];
  747.     [pppLogScroll setTextAttributeGray:NX_BLACK];
  748. }
  749.  
  750. // -------------------------------------------------------------------------------------
  751. // user commands
  752.  
  753. /* connect */
  754. - (void)connect:(id)sender
  755. {
  756.  
  757.     /* disconnect */
  758.     if (connectExeId) {
  759.         if (!CMD_DISCONNECT) {
  760.             [self message:1:"'Disconnect' command has not been specified"];
  761.             return;
  762.         }
  763.         [self message:0:"Disconnecting ... "];
  764.         [autoConnect setState:0]; // clear auto-reconnect
  765.         [btnConnect setEnabled:NO];
  766.         [self _enableCheckConnect:NO]; // cancel connection check
  767.         [self _enablePeriodicPing:NO]; // cancel ping
  768.         [self _enableConnectClock:NO]; // cancel connection timer
  769.         _isDisconnecting = YES;
  770.         if (!_testMode_) {
  771.             if (SYSTEM(CMD_DISCONNECT)) {
  772.                 [self message:1:"'Disconnect' command failed"];
  773.                 _isDisconnecting = NO;
  774.                 [self enableConnectButton:self]; // delayed
  775.             }
  776.         } else {
  777.             [self commandDidComplete:self withError:0];
  778.         }
  779.         return;
  780.     }
  781.     _isDisconnecting = NO;
  782.     
  783.     /* ignore connect-request during shutdown */
  784.     if (_shutDown) return;
  785.     
  786.     /* load PPP driver is necessary */
  787.     if (!_testMode_ && ![self isPPPDriverLoaded]) {
  788.         if (CMD_LOADPPPLKS) {
  789.             [self message:0:"Loading PPP driver ... "];
  790.             { [[appMatrix window] flushWindow]; NXPing(); }
  791.             if (SYSTEM(CMD_LOADPPPLKS)) {
  792.                 [self message:1:"Error encountered while loading PPP driver"];
  793.             } else {
  794.                 [self message:0:"Loading PPP driver ... successful"];
  795.             }
  796.             if (![self isPPPDriverLoaded]) return;
  797.         } else {
  798.             [self message:1:"PPP driver must be loaded before connecting"];
  799.             return;
  800.         }
  801.     }
  802.     
  803.     /* check for 'Connect' command availability */
  804.     if (!CMD_CONNECT) {
  805.         [self message:1:"'Connect' command has not been specified"];
  806.         return;
  807.     }
  808.     
  809.     /* check for already connected */
  810.     if (![self isReallyConnected]) {
  811.         [self message:0:"Connecting ..."];
  812.         _connectStart = _intervalTime();
  813.         if (!_testMode_) {
  814.             connectExeId = [ExecRunCommand runCommand:CMD_CONNECT output:self];
  815.             if (!connectExeId) {
  816.                 [self message:1:"Connection failed to start"];
  817.             } else {
  818.                 [self _enableCheckConnect:YES];
  819.             }
  820.         } else {
  821.             connectExeId = self;
  822.             [self _enableCheckConnect:YES];
  823.         }
  824.     } else {
  825.         _alreadyConnected = YES;
  826.         [self message:1:"Already connected"];
  827.     }
  828.     [self enableConnectButton:self]; // delayed
  829.     
  830. }
  831.  
  832. /* run application */
  833. - (void)runApplication:(id)sender
  834. {
  835.     id btnCell = [appMatrix selectedCell];
  836.     int appNdx = [btnCell tag];
  837.     if ((appNdx >= 0) && (appNdx < netAppCount) && netApps[appNdx]) {
  838.         const char *app = netApps[appNdx];
  839.         int r = -1, c = -1;
  840.         NXRect cellFrame;
  841.         [appMatrix getRow:&r andCol:&c ofCell:btnCell];
  842.         [appMatrix getCellFrame:&cellFrame at:r:c];
  843.         [appMatrix lockFocus];
  844.         _hiliteRect(&cellFrame);
  845.         { [[appMatrix window] flushWindow]; NXPing(); }
  846.         if (_cmdIsApp(app)) {
  847.             char buff[1024], *cmdFile = (char*)_getCmdFile(buff,app);
  848.             NXPortFromName(cmdFile,(char*)nil);
  849.         } else {
  850.             const char *cmdLine = _cmdLine(app);
  851.             char *ext = ";\n", *cmd = (char*)malloc(strlen(cmdLine)+strlen(ext)+1);
  852.             sprintf(cmd,"%s%s",cmdLine,ext);
  853.             [ExecMonitor terminalCommand:cmd title:"Command"];
  854.             free(cmd);
  855.         }
  856.         [btnCell setDrawDots:NO];
  857.         [btnCell drawSelf:&cellFrame inView:appMatrix]; 
  858.         { [[appMatrix window] flushWindow]; NXPing(); }
  859.         [appMatrix unlockFocus];
  860.     } else NXBeep();
  861. }
  862.  
  863. // -------------------------------------------------------------------------------------
  864. // command execution/completion
  865. // -------------------------------------------------------------------------------------
  866.  
  867. /* command output (when we are the delegate) */
  868. - (void)commandOutput:(id)execId buffer:(const char*)buffer len:(int)len
  869. {
  870.     /* ignore anything we get */
  871. }
  872.  
  873. /* call-back from shell command (main thread) */
  874. - (void)commandDidComplete:execId withError:(int)err
  875. {
  876.  
  877.     /* check for connection termination */
  878.     if (execId == connectExeId) {
  879.         _isConnected = NO;
  880.         connectExeId = nil;
  881.         [NSApp setApplicationIconImage:[NXImage findImageNamed:"appIcon"]];
  882.         if (_isDisconnecting) { // manual disconnect
  883.             [self message:0:"Connection terminated"];
  884.         } else
  885.         if ((_intervalTime() - _connectStart) <= 7) { // failed in <= X seconds
  886.             if ([self isPPPDRunning]) { // pppd still running
  887.                 _alreadyConnected = YES;
  888.                 [exeWindow makeKeyAndOrderFront:(id)nil];
  889.                 [self message:1:"'pppd' still running "
  890.                     "('-detach' apparently not specified)"];
  891. #if 1
  892.                 [autoConnect setState:0];
  893. #else
  894.                 // Assuming that '-detach' was not specified on the 'pppd' command:
  895.                 // At this point the 'pppd' command will be monitored to attempt to detect 
  896.                 // detect when it is destroyed. We attempt to simulate the same interface 
  897.                 // used in 'ExecRunServer' to monitor it's own child processes.
  898.                 connectExeId = self;
  899.                 [self _enablePPPDMonitor:YES];
  900.                 _isConnected = [self isReallyConnected];
  901.                 return;
  902. #endif
  903.             } else {
  904.                 [self message:1:"Connection did not start properly"];
  905.             }
  906.         } else
  907.         if ([autoConnect state]) { // re-connect
  908.             PLAY(SND_DISCONNECT);
  909.             [self message:1:"Connection terminated (auto-reconnect)"];
  910.             [self _perform:@selector(connect:):nil delay:1000];
  911.         } else {
  912.             PLAY(SND_DISCONNECT);
  913.             [self message:1:"Connection terminated"];
  914.         }
  915.         _isDisconnecting = NO;
  916.         [exeWindow makeKeyAndOrderFront:(id)nil];
  917.         [self enableConnectButton:self];
  918.         return;
  919.     }
  920.         
  921.     /* check for ping */
  922.     if (execId == pingExeId) {
  923.         pingExeId = nil;
  924.         [pingMessage setStringValue:""];
  925.         return;
  926.     }
  927.  
  928.     /* ppp log scroller */
  929.     if (execId == pppLogExeId) {
  930.         [self logMessage:err:"'tail' command terminated.\n"];
  931.         pppLogExeId = nil;
  932.         return;
  933.     }
  934.  
  935.     /* error */
  936.     fprintf(stderr,"Unknown execId in call to 'commandDidComplete:withError:'");
  937.     return;
  938. }
  939.  
  940. // -------------------------------------------------------------------------------------
  941. // output monitor
  942. // -------------------------------------------------------------------------------------
  943.  
  944. /* same as "strstr()", except with limit check */
  945. const char *strnstr(const char *s1, const char *s2, int n)
  946. {
  947.     int i, n2 = strlen(s2), e = n - n2;
  948.     for (i = 0; i < e; i++) {
  949.         while ((i < e) && (s1[i] != s2[0])) i++;
  950.         if ((i < e) && !strncmp(&s1[i],s2,n2)) return &s1[i];
  951.     }
  952.     return (char*)0;
  953. }
  954.  
  955. /* monitor scroll text output */
  956. - (void)monitorOutput:scrollId buffer:(const char*)buffer len:(int)len
  957. {
  958.     if (scrollId != pppLogScroll) return;
  959.     // not fully implemented
  960. }
  961.  
  962. // -------------------------------------------------------------------------------------
  963. // Terminal invoke
  964. // -------------------------------------------------------------------------------------
  965.  
  966. /* invoke command in Terminal window */
  967. + terminalCommand:(const char*)cmd title:(const char*)title
  968. {
  969.     Speaker    *speaker;
  970.     port_t    terminalPort;
  971.     
  972.     /* can't find Terminal */
  973.     if (!(terminalPort = NXPortFromName("Terminal", NULL))) return (id)nil;
  974.     
  975.     /* launch Terminal */
  976.     [NSApp deactivateSelf];
  977.     creat("/tmp/.reallyignorethis.term", 0444);
  978.     [[Application workspace] openFile:"/tmp/.reallyignorethis.term"
  979.         fromImage:(id)nil at:(NXPoint*)nil inView:(id)nil];
  980.     
  981.     /* run command */
  982.     speaker = [NSApp appSpeaker];
  983.     [speaker setSendPort: terminalPort];
  984.     [speaker selectorRPC:"runCommand:usingShell:inFolder:windowTitle:closeOnExit:"
  985.         paramTypes: "cccci", cmd, "", "", title, NO];
  986.  
  987.     return self;
  988.     
  989. }
  990.  
  991. // -------------------------------------------------------------------------------------
  992.  
  993. @end
  994.  
  995. // -------------------------------------------------------------------------------------
  996. // TileButtonCell
  997. // -------------------------------------------------------------------------------------
  998. @implementation TileButtonCell
  999. - drawSelf:(const NXRect *)cellFrame inView:controlView
  1000. {
  1001.     NXSize imageSize;
  1002.     NXPoint imageOrigin, iconOrigin = cellFrame->origin;
  1003.     
  1004.     /* draw tile */
  1005.     iconOrigin.y += cellFrame->size.height; // assume frameHeight == tileHeight
  1006.     if (shelfTile) { [shelfTile composite:NX_COPY toPoint:&iconOrigin]; } 
  1007.     else { NXDrawButton(cellFrame, cellFrame); }
  1008.     
  1009.     /* draw image */
  1010.     [[self image] getSize:&imageSize];
  1011.     imageOrigin.x = iconOrigin.x + (cellFrame->size.width  - imageSize.width )/2.0;
  1012.     imageOrigin.y = iconOrigin.y - (cellFrame->size.height - imageSize.height)/2.0;
  1013.     [[self image] composite:NX_SOVER toPoint:&imageOrigin];
  1014.     
  1015.     /* draw text */
  1016.     if ([self iconPosition] == NX_ICONABOVE) {
  1017.         NXSize titleSize;
  1018.         NXRect titleRect = *cellFrame;
  1019.         static Cell *titleCell = nil;
  1020.         if (!titleCell) {
  1021.             titleCell = [[Cell alloc] initTextCell:""];
  1022.             [titleCell setAlignment:NX_CENTERED];
  1023.             [titleCell setFont:[controlView font]];
  1024.         }
  1025.         [titleCell setStringValue:[self title]];
  1026.         [titleCell calcCellSize:&titleSize inRect:cellFrame];
  1027.         titleRect.origin.y += titleRect.size.height - titleSize.height - 7;
  1028.         titleRect.origin.x += floor((titleRect.size.width-titleSize.width)/2.0);
  1029.         titleRect.size.width  = titleSize.width;
  1030.         titleRect.size.height = titleSize.height;
  1031.         [titleCell drawSelf:&titleRect inView:controlView];
  1032.     }
  1033.     
  1034.     /* draw dots */
  1035.     if (shelfDots && !_noDots) [shelfDots composite:NX_SOVER toPoint:&iconOrigin];
  1036.     
  1037.     return self;
  1038. }
  1039. - highlight:(const NXRect*)cellFrame inView:aView lit:(BOOL)flag { return self; }
  1040. - (void)setDrawDots:(BOOL)flag { _noDots = flag? NO : YES; }
  1041. @end
  1042.